﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Crypto.Parameters;
using iTextSharp.text.pdf.security;
using com.itextpdf.text.pdf.security;
using System.Security.Cryptography;

namespace HTToPdf
{
    public class Common
    {

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="folderPdf"></param>
        /// <param name="pathToNewSignFile"></param>
        /// <param name="pathToCerts"></param>
        /// <param name="nameCert"></param>
        /// <param name="passCert"></param>
        private void SignPdf(string filename, string folderPdf, string pathToNewSignFile, string pathToCerts, string nameCert, string passCert)
        {
            var pathToCert = GetFullNameFile(pathToCerts, nameCert); //Oh.. I did not know about the Path.Combine function.

            if (!File.Exists(pathToCert))
            {
                //logger.Error("Certificate not exist " + pathToCert);
                WriteLogs("证书文件" + pathToCert + "不存在", false);
                return;
            }

            var pass = passCert.ToCharArray();

            FileStream fs;
            try
            {
                fs = new FileStream(pathToCert, FileMode.Open);
            }
            catch (Exception ex)
            {
                //logger.Error(ex, "Could not open cert" + pathToCert);
                WriteLogs("无法打开证书：" + pathToCert, false);
                return;
            }

            var store = new Pkcs12Store(fs, pass);

            fs.Close();

            var alias = "";

            // searching for private key
            foreach (string al in store.Aliases)
                if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate)
                {
                    alias = al;
                    break;
                }

            var pk = store.GetKey(alias);

            ICollection<X509Certificate> chain = store.GetCertificateChain(alias).Select(c => c.Certificate).ToList();

            var parameters = pk.Key as RsaPrivateCrtKeyParameters;

            var pathPdf = GetFullNameFile(folderPdf, filename); //Oh.. I did not know about the Path.Combine function.

            var pathToSigPdf = GetFullNameFile(pathToNewSignFile, filename);

            if (!File.Exists(pathPdf))
            {
                //logger.Error("Could not open file" + pathPdf + "  File not exist");
                WriteLogs("无法找到证书文件：" + pathPdf, false);
                return;
            }

            var reader = new PdfReader(pathPdf);

            FileStream fileStreamSigPdf;
            try
            {
                fileStreamSigPdf = new FileStream(pathToSigPdf, FileMode.Create);
            }
            catch (Exception ex)
            {

                //logger.Error(ex, "Could not create file" + pathToSigPdf);
                WriteLogs("无法创建证书文件：" + pathToSigPdf, false);
                return;
            }

            var stamper = PdfStamper.CreateSignature(reader, fileStreamSigPdf, '\0', null, true);

            var appearance = stamper.SignatureAppearance;
            appearance.Reason = "Утверждено";

            appearance.SetVisibleSignature("---> ExistSignatureName <-----");

            IExternalSignature pks = new PrivateKeySignature(parameters, DigestAlgorithms.SHA256);
            MakeSignature.SignDetached(appearance, pks, chain, null, null, null, 0, CryptoStandard.CMS);

            fileStreamSigPdf.Close();
            reader.Close();
            stamper.Close();

            //logger.Info("Signed successfully " + filename);
            WriteLogs("创建证书文件完成:"+filename,false);
        }

        private string GetFullNameFile(string folder, string file)
        {
            return folder+"\\"+file;
        }

        public string PrepareSignatureAndGetHash()
        {
            var hash = string.Empty;

            using (var reader = new PdfReader("PathTemplate"))
            {
                using (var fileStream = File.OpenWrite("PathToTemp"))
                {
                    using (var stamper = PdfStamper.CreateSignature(reader, fileStream, '0', null, true))
                    {
                        var signatureAppearance = stamper.SignatureAppearance;
                        signatureAppearance.SetVisibleSignature("ExistSignatureName");
                        IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
                        signatureAppearance.Reason = "Sig";
                        signatureAppearance.Layer2Text = "Super SIG";

                        signatureAppearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;

                        MakeSignature.SignExternalContainer(signatureAppearance, external, 8192);


                        using (var contentStream = signatureAppearance.GetRangeStream())
                        {
                            hash = string.Join(string.Empty, SHA1.Create().ComputeHash(contentStream).Select(x => x.ToString("X2")));
                        }
                    }
                }
            }

            return hash;
        }

        public void SigSignature(string base64String)
        {
            using (var reader = new PdfReader("PathToTemp"))
            {
                using (var fileStream = File.OpenWrite("PathToSig"))
                {
                    var byteSig = Convert.FromBase64String(base64String);
                    IExternalSignatureContainer external = new MfuaExternalSignatureContainer(byteSig);

                    MakeSignature.SignDeferred(reader, "ExistSignatureName", fileStream, external);
                }
            }
        }

        private class MfuaExternalSignatureContainer : IExternalSignatureContainer
        {
            private readonly byte[] _signedBytes;

            public MfuaExternalSignatureContainer(byte[] signedBytes)
            {
                _signedBytes = signedBytes;
            }

            public byte[] Sign(Stream data)
            {
                return _signedBytes;
            }

            public void ModifySigningDictionary(PdfDictionary signDic)
            {
            }
        }


        #region 模板
        public class Model
        {
            string _djbh = string.Empty;
            string _rq = string.Empty;
            string _dwmch = string.Empty;
            string _hsje = string.Empty;
            string _zkzrje = string.Empty;
            string _thfs = string.Empty;
            string _yunfcd = string.Empty;
            string _fkfs = string.Empty;
            string _jsms = string.Empty;
            string _faplx = string.Empty;
            string _username = string.Empty;
            string _dzhdh = string.Empty;
            string _khyh = string.Empty;
            string _yhzhh = string.Empty;
            string _shh = string.Empty;
            string _fhzq = string.Empty;
            string _yyzq = string.Empty;
            public string djbh { get { return _djbh; } set { _djbh = value; } }
            public string rq { get { return _rq; } set { _rq = value; } }
            public string dwmch { get { return _dwmch; } set { _dwmch = value; } }
            public string hsje { get { return _hsje; } set { _hsje = value; } }
            public string zkzrje { get { return _zkzrje; } set { _zkzrje = value; } }
            public string thfs { get { return _thfs; } set { _thfs = value; } }
            public string yunfcd { get { return _yunfcd; } set { _yunfcd = value; } }
            public string fkfs { get { return _fkfs; } set { _fkfs = value; } }
            public string jsms { get { return _jsms; } set { _jsms = value; } }
            public string faplx { get { return _faplx; } set { _faplx = value; } }
            public string username { get { return _username; } set { _username = value; } }
            public string dzhdh { get { return _dzhdh; } set { _dzhdh = value; } }
            public string khyh { get { return _khyh; } set { _khyh = value; } }
            public string yhzhh { get { return _yhzhh; } set { _yhzhh = value; } }
            public string shh { get { return _shh; } set { _shh = value; } }
            public string fhzq { get { return _fhzq; } set { _fhzq = value; } }
            public string yyzq { get { return _yyzq; } set { _yyzq = value; } }
        }
        #endregion

        #region 错误日志
        public void WriteLog(string msg, bool isdebug)
        {
            string text = AppDomain.CurrentDomain.BaseDirectory;
            text = Path.Combine(text, "log");
            if (!Directory.Exists(text))
            {
                Directory.CreateDirectory(text);
            }
            if (isdebug)
            {
                text = Path.Combine(text, "debug");
                if (!Directory.Exists(text))
                {
                    Directory.CreateDirectory(text);
                }
            }
            text = Path.Combine(text, DateTime.Now.ToString("yyyyMMdd") + ".txt");
            if (!File.Exists(text))
            {
                File.CreateText(text).Close();
            }
            StreamWriter streamWriter = new StreamWriter(text, true, Encoding.UTF8);
            if (isdebug)
            {
                streamWriter.WriteLine("--------------------------------------------------------------------------------------------------------------------------------");
            }
            streamWriter.WriteLine("{0}  {1}", DateTime.Now.ToString("yyyyMMdd HH:mm:ss"), msg);
            if (isdebug)
            {
                streamWriter.WriteLine("--------------------------------------------------------------------------------------------------------------------------------");
            }
            streamWriter.Flush();
            streamWriter.Close();
        }

        public static void WriteLogs(string msg, bool isdebug)
        {
            string text = AppDomain.CurrentDomain.BaseDirectory;
            text = Path.Combine(text, "log");
            if (!Directory.Exists(text))
            {
                Directory.CreateDirectory(text);
            }
            if (isdebug)
            {
                text = Path.Combine(text, "debug");
                if (!Directory.Exists(text))
                {
                    Directory.CreateDirectory(text);
                }
            }
            text = Path.Combine(text, DateTime.Now.ToString("yyyyMMdd") + ".txt");
            if (!File.Exists(text))
            {
                File.CreateText(text).Close();
            }
            StreamWriter streamWriter = new StreamWriter(text, true, Encoding.UTF8);
            if (isdebug)
            {
                streamWriter.WriteLine("--------------------------------------------------------------------------------------------------------------------------------");
            }
            streamWriter.WriteLine("{0}  {1}", DateTime.Now.ToString("yyyyMMdd HH:mm:ss"), msg);
            if (isdebug)
            {
                streamWriter.WriteLine("--------------------------------------------------------------------------------------------------------------------------------");
            }
            streamWriter.Flush();
            streamWriter.Close();
        }

        public void ClearLog()
        {
            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log");
            if (Directory.Exists(path))
            {
                DirectoryInfo directoryInfo = new DirectoryInfo(path);
                FileInfo[] files = directoryInfo.GetFiles("*.*", SearchOption.AllDirectories);
                FileInfo[] array = files;
                for (int i = 0; i < array.Length; i++)
                {
                    FileInfo fileInfo = array[i];
                    if (DateTime.Now.Subtract(fileInfo.CreationTime).Days > 30)
                    {
                        File.Delete(fileInfo.FullName);
                    }
                }
            }
        }
        #endregion

        #region 金额大写
        /// <summary>
        /// 金额大写
        /// </summary>
        /// <param name="s">金额小写数字</param>
        /// <returns>中文金额大写</returns>
        public string NumtoChinese(decimal s)
        {
            string l = "";
            l = s >= 0 ? "" :"负";
            s = s > 0 ? s : Math.Abs(s);

            s = Math.Round(s, 2);//四舍五入到两位小数，即分
            string[] n = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
            //数字转大写
            string[] d = { "", "分", "角", "元", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿" };
            //不同位置的数字要加单位
            List<string> needReplace = new List<string> { "零拾", "零佰", "零仟", "零万", "零亿", "亿万", "零元", "零零", "零角", "零分" };
            List<string> afterReplace = new List<string> { "零", "零", "零", "万", "亿", "亿", "元", "零", "", "" };//特殊情况用replace剔除
            string e = s % 1 == 0 ? "整" : "";//金额是整数要加一个“整”结尾
            string re = "";
            Int64 a = (Int64)(s * 100);
            int k = 1;
            while (a != 0)
            {//初步转换为大写+单位
                re = n[a % 10] + d[k] + re;
                a = a / 10;
                k = k < 11 ? k + 1 : 4;
            }
            string need = needReplace.Where(tb => re.Contains(tb)).FirstOrDefault<string>();
            while (need != null)
            {
                int i = needReplace.IndexOf(need);
                re = re.Replace(needReplace[i], afterReplace[i]);
                need = needReplace.Where(tb => re.Contains(tb)).FirstOrDefault<string>();
            }//循环排除特殊情况
            re = re == "" ? "" : re + e;
            return l+re;
        }
        #endregion
    }
}